// 对象原始转换规则：
// 1.布尔值转换：所有对象在布尔值转换中均为 true。
// 2.数值转换：数字转换发生在对象使用数学运算符时。
// 3.字符串转换：发生在隐式 alert() 或者 显示 String() .toString()，或者与其他字符串拼接时。

// ***********************************************************************************************

// hint 应用哪种转换：
// 1. string：发生在隐式或显示转换成字符串时，会使用 "string" hint。
// 2. number：除了 二元加法。其他会发生在 +obj（一元加），-（减），*（乘）和 /（除），>（大于）、<（小于）等...，会使用 "number" hint。
// 3. boolean：对象都转换为 true。
// 3. default：二元加法（因为不确定是数字还是字符串相加），或 == 比较时，会使用 "default" hint。

// ***********************************************************************************************

// 为了进行转换，JS 会尝试并调用三个对象方法：
// 1. 调用 obj[Symbol.toPrimitive](hint) ——— 如果这个方法存在的话（系统内建 symbol）。
// 2. 否则，如果 hint 是 string —— 则尝试调用 obj.toString()[存在则优先调用] 或 obj.valueOf()，无论那个存在。
// 3. 否则，如果 hint 是 number 或 default —— 则尝试调用 obj.valueOf()[存在则优先调用] 或 obj.toString()，无论那个存在。

// ***********************************************************************************************

// * 【SymbolSymbol.toPrimitive】
obj[Symbol.toPrimitive] = function (hint) {
	// hint = "string" | "number" | "default" 中的一个。
	// 编写将对象转成原始值的代码
	// “它必须返回一个原始值” 否则报错
}

// 如果 Symbol.toPrimitive 方法存在，则它会被用于所有 hint，无需更多其他方法。

// 例子：
let user = {
	name: 'Ben',
	money: 1000,

	[Symbol.toPrimitive](hint) {
		console.log('hint:', hint)
		return hint === 'string' ? `{name: ${this.name}}` : this.money
	},
}

// hint: stirng
console.log(String(user)) //"{name: Ben}"

// hint: number
console.log(+user) // 1000

// hint: default
console.log(user + 500) // 1500
console.log(user == 1000) // true

// * 【toString/valueOf】
// 如果 Symbol.toPrimitive 不存在，那么 JS 将尝试寻找 toString 或 valueOf 方法。
// 对于 "sting":hint 优先调用 toString，如果不存在则调用 valueOf。
// 对于 "number":hint 和 "default":hint 优先调用 valueOf，如果不存在则调用 toString。

// toString 和 valueOf “必须返回一个原始值”，如果主动返回一个对象，那么返回值会被忽略（但不会报错）。
// 默认情况下，普通对象具有 toString 和 valueOf 方法。
// obj.valueOf() 方法会返回对象自身。

// 示例1：
let obj = { name: 'Jake' }
let arr = [1, 2, 3, { name: 'obj' }]
console.log(String(obj)) // "[object object]"
console.log(String(arr)) // '1,2,3,[object object],[object object]'
console.log(obj.valueOf() === obj) // true，obj.valueOf() 返回对象自身
console.log(arr === arr.valueOf()) //true
console.log(+obj === obj) // false，+obj 会被转换为 NaN

// 示例2：
let obj2 = {
	name: 'Jake',
	toString() {
		return 27
	},
}
console.log(Number(obj2)) // 27
/*
	原因：
	1.当转成number时默认调用valueOf
	2.由于valueOf返回的是对象自身，所以被忽略，然后接着去调用toString方法（我们覆盖了默认的toString）。
	所以结果为 27。
*/

// ***********************************************************************************************

// 通常我们希望有一个“全能”的地方来处理所有原始转换。在这种情况下，我们可以只自定义实现 toString 方法。
// 在实际使用中，通常只实现 obj.toString() 作为字符串转换的“全能”方法就足够了

let users = {
	name: 'Jake',
	toString() {
		return 1
	},
}

// 首先调用 valueOf 方法，该方法返回的不是一个原始值，而是返回的对象自身，所以 JavaScript 接着调用自定义的 toString() 方法。
console.log(+users) // 1
